import { cloneDeep } from 'lodash-es';
import { MODEL_NAME } from '../../constants/model-names';
import {
  COMMON_SECT_TASKS,
  LIAN_DAN_SECT_TASKS,
  LIAN_QI_SECT_TASKS,
} from '../../data/sect/task';
import {
  SECT_CULTIVATION_SPOTS_DATA,
  SECT_LEVEL_MULTIPLIER,
  SECT_TAG,
} from '../../data/sect/type';
import { EVENTS, type IEventBus } from '../../events';
import type { OfflineEarningsModel, SectModel, WalletModel } from '../../model';
import { REWARD_SOURCE_TYPE, REWARD_TYPE } from '../../model/reward/type';
import type {
  SectListItem,
  SectScriptureListItem,
  SectTaskListItem,
} from '../../model/sect/type';
import { BaseSystem } from '../base';
import type { PlayerStateSystem } from '../player-state';
import { CURRENCY_TYPE } from '../../model/wallet';
import type { OflineEarningsSectHangUpTaskInfo } from '../../model/offline-earnings/type';

export class SectSystem extends BaseSystem {
  private bus: IEventBus;
  private playerStateSystem: PlayerStateSystem;

  constructor(bus: IEventBus, playerStateSystem: PlayerStateSystem) {
    super();
    this.bus = bus;
    this.playerStateSystem = playerStateSystem;

    this.bus.on(EVENTS.SECT_JOIN, (sectId: string) => {
      const sectModel = this.playerStateSystem.getModel<SectModel>(
        MODEL_NAME.SECT
      );
      sectModel.joinSect(sectId);
    });

    this.bus.on(EVENTS.SECT_HANG_UP_TASK_START, (id: string) => {
      const sectModel = this.playerStateSystem.getModel<SectModel>(
        MODEL_NAME.SECT
      );
      sectModel.startHangUpTask(id);
    });

    this.bus.on(EVENTS.SECT_HANG_UP_TASK_RESET_PROGRESS, () => {
      const sectModel = this.playerStateSystem.getModel<SectModel>(
        MODEL_NAME.SECT
      );
      sectModel.resetProgress();
    });

    this.bus.on(EVENTS.SECT_HANG_UP_TASK_COMPLETED, () => {
      this.sectHangUpTaskCompleted();
    });

    this.bus.on(EVENTS.SECT_JOIN_SUCCESS, () => {
      this.updateSectList();
      this.updateSectScriptureList();
    });
    this.bus.on(EVENTS.REALM_BREAKTHROUGH, () => {
      this.updateSectList();
    });
    this.bus.on(EVENTS.SECT_CULTIVATION_SPOT_ADD_VALID_COUNT, () => {
      this.addCultivationSpotValidCount();
    });
    this.bus.on(EVENTS.SECT_JOB_UPGRADE, () => {
      this.upgradeJob();
      this.updateSectList();
      this.updateSectScriptureList();
    });
    this.bus.on(
      EVENTS.SECT_EXCHANGE_SCRIPTURE_ITEM,
      (item: SectScriptureListItem) => {
        this.exchangeScriptureItem(item);
      }
    );
    this.bus.on(EVENTS.REWARD_ISSUE_COMPLETED, (sourceType) => {
      if (sourceType !== REWARD_SOURCE_TYPE.SECT_SCRIPTURE_EXCHANGE) return;
      this.updateSectScriptureList();
    });
  }

  /** 更新宗门列表 */
  private updateSectList() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    const sectConfigs = sectModel.getSectConfigs();
    const sectList = sectConfigs.map<SectListItem>((sect) => {
      /** 宗门修炼场地配置 */
      const cultivationSpotConfig = SECT_CULTIVATION_SPOTS_DATA[sect.level];

      const conditionProgress = this.playerStateSystem.getConditionsProgress(
        sect.conditions || []
      );

      const canJoin =
        conditionProgress.length === 0
          ? true
          : conditionProgress.every((progress) => progress.isMet);

      const sectTags = sect.tags;
      /** 所有宗门相关任务 */
      let sectTaskConfigList = [...COMMON_SECT_TASKS];
      sectTags.forEach((tag) => {
        switch (tag) {
          case SECT_TAG.LIAN_DAN:
            sectTaskConfigList.push(...LIAN_DAN_SECT_TASKS);
            break;
          case SECT_TAG.LIAN_QI:
            sectTaskConfigList.push(...LIAN_QI_SECT_TASKS);
            break;
          default:
            break;
        }
      });
      const sectTaskList = sectTaskConfigList.map<SectTaskListItem>((task) => ({
        config: task,
        canReceive: this.playerStateSystem.checkConditions(
          task.receiveConditions || []
        ),
      }));

      return {
        config: sect,
        cultivationSpotConfig,
        canJoin,
        conditionProgress,
        sectTaskList,
      };
    });
    sectModel.setSectList(sectList);
  }

  /** 更新藏书阁列表 */
  public updateSectScriptureList() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    const currentSectConfig = sectModel.getCurrentSect();
    if (!currentSectConfig) return;
    /** 当前宗门贡献  */
    const sectContribution = sectModel.getCurrentSectContribution();
    const sectScriptureList =
      currentSectConfig.config.scriptureHall.map<SectScriptureListItem>(
        (item) => {
          const canReceive =
            sectContribution >= item.contribution &&
            this.playerStateSystem.checkConditions(
              item.exchangeCondition || []
            );
          return {
            config: item,
            canReceive,
            exchangeConditionProgress:
              this.playerStateSystem.getConditionsProgress(
                item.exchangeCondition || []
              ),
          };
        }
      );
    sectModel.setSectScriptureList(sectScriptureList);
  }

  /** 更新挂机宗门任务 */
  public updateHangUpTask() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    const hangUpTaskId = sectModel.getCurrentHangUpTaskId();
    if (!hangUpTaskId) return;
    sectModel.startHangUpTask(hangUpTaskId);
  }

  private sectHangUpTaskCompleted() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    const currentSect = sectModel.getCurrentSect();
    if (!currentSect) return;
    const currentHangUpTaskConfig = sectModel.getCurrentHangUpTaskConfig();
    if (!currentHangUpTaskConfig) return;
    const currentSectConfig = currentSect.config;
    const sectLevel = currentSectConfig.level;
    const sectLevelMultiplier = SECT_LEVEL_MULTIPLIER[sectLevel];
    const currentSectJobConfig = sectModel.getCurrentSectJob();
    if (!currentSectJobConfig) return;
    const jobMultiplier = currentSectJobConfig.config.multiplier;
    const baseReward = currentHangUpTaskConfig?.config.baseReward || [];
    const rewardMultiplier = sectLevelMultiplier * jobMultiplier;
    const reward = baseReward.map((reward) => {
      switch (reward.type) {
        case REWARD_TYPE.SPIRIT_STONE:
          const newReward = cloneDeep(reward);
          newReward.quantity = reward.quantity * rewardMultiplier;
          return newReward;
        default:
          return reward;
      }
    });
    this.updateSectScriptureList();
    this.bus.emit(
      EVENTS.REWARD_ISSUE,
      reward,
      REWARD_SOURCE_TYPE.SECT_HANG_UP_TASK
    );
  }

  /** 增加修炼次数 */
  private addCultivationSpotValidCount() {
    const walletModel = this.playerStateSystem.getModel<WalletModel>(
      MODEL_NAME.WALLET
    );
    if (!walletModel) return;
    const spiritStone = walletModel.getBalance(CURRENCY_TYPE.SPIRIT_STONE);
    if (spiritStone < 1000) return;
    walletModel.spendCurrency(CURRENCY_TYPE.SPIRIT_STONE, 1000);
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    sectModel.buySectCultivationSpotValidCount();
  }

  /** 职位升级 */
  private upgradeJob() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    const currentSectJobConfig = sectModel.getCurrentSectJob();
    if (!currentSectJobConfig) return;
    const nextContribution = currentSectJobConfig.config.nextContribution;
    const currentContribution = sectModel.getCurrentSectContribution();
    if (!nextContribution || nextContribution > currentContribution) return;
    sectModel.upgradeSectJob();
    sectModel.reduceSectContribution(nextContribution);
  }

  /** 设置宗门挂机任务离线收益 */
  private setSectHangUpOfflineEarnings() {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    const offlineEarningsModel =
      this.playerStateSystem.getModel<OfflineEarningsModel>(
        MODEL_NAME.OFFLINE_EARNINGS
      );
    if (!sectModel || !offlineEarningsModel) return;
    const currentSect = sectModel.getCurrentSect();
    if (!currentSect) return;
    const currentHangUpTaskConfig = sectModel.getCurrentHangUpTaskConfig();
    if (!currentHangUpTaskConfig) return;
    const currentSectConfig = currentSect.config;
    const sectLevel = currentSectConfig.level;
    const sectLevelMultiplier = SECT_LEVEL_MULTIPLIER[sectLevel];
    const currentSectJobConfig = sectModel.getCurrentSectJob();
    if (!currentSectJobConfig) return;
    const jobMultiplier = currentSectJobConfig.config.multiplier;
    const hangUpTaskIncomeTime = sectModel.getHangUpTaskIncomeTime();
    const offlineDiffSeconds = offlineEarningsModel.getOfflineDiffTime() / 1000;
    const taskCount = Math.floor(offlineDiffSeconds / hangUpTaskIncomeTime);
    const baseReward = currentHangUpTaskConfig?.config.baseReward || [];
    const rewardMultiplier = sectLevelMultiplier * jobMultiplier;
    const reward = baseReward.map((reward) => {
      switch (reward.type) {
        case REWARD_TYPE.SPIRIT_STONE:
          const newReward = cloneDeep(reward);
          newReward.quantity = reward.quantity * rewardMultiplier;
          return newReward;
        default:
          return reward;
      }
    });
    const finallyReward = reward.map((reward) => {
      const newReward = cloneDeep(reward);
      switch (newReward.type) {
        case REWARD_TYPE.SECT_CONTRIBUTION:
          newReward.quantity = newReward.quantity * taskCount;
          return newReward;
        case REWARD_TYPE.SPIRIT_STONE:
          newReward.quantity = newReward.quantity * taskCount;
          return newReward;
        default:
          return newReward;
      }
    });
    const sectHangUpTaskInfo: OflineEarningsSectHangUpTaskInfo = {
      sectContribution: 0,
      spiritualStone: 0,
    };
    finallyReward.forEach((reward) => {
      switch (reward.type) {
        case REWARD_TYPE.SECT_CONTRIBUTION:
          sectHangUpTaskInfo.sectContribution += reward.quantity;
          break;
        case REWARD_TYPE.SPIRIT_STONE:
          sectHangUpTaskInfo.spiritualStone += reward.quantity;
          break;
        default:
          break;
      }
    });
    offlineEarningsModel.setSectHangUpTaskInfo(sectHangUpTaskInfo);
    const earnings = offlineEarningsModel.getEarnings();
    offlineEarningsModel.setSectContribution(
      earnings.sectContribution.value + sectHangUpTaskInfo.sectContribution
    );
    offlineEarningsModel.setSpiritualStone(
      earnings.spiritualStone.value + sectHangUpTaskInfo.spiritualStone
    );
  }
  private exchangeScriptureItem(item: SectScriptureListItem) {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    if (!sectModel) return;
    const reward = item.config.reward;
    if (!reward) return;
    const currentContribution = sectModel.getCurrentSectContribution();
    if (currentContribution < item.config.contribution) return;
    sectModel.reduceSectContribution(item.config.contribution);
    this.bus.emit(
      EVENTS.REWARD_ISSUE,
      [reward],
      REWARD_SOURCE_TYPE.SECT_SCRIPTURE_EXCHANGE
    );
    this.bus.emit(EVENTS.GAME_MESSAGE, `兑换 [${reward.name}] 成功`);
  }

  public override init(): void {
    this.updateSectList();
    this.updateHangUpTask();
    this.updateSectScriptureList();
  }

  public override initAfter(): void {
    this.setSectHangUpOfflineEarnings();
  }

  public override update(dt: number): void {
    const sectModel = this.playerStateSystem.getModel<SectModel>(
      MODEL_NAME.SECT
    );
    sectModel.updateHangUpTask(dt);
  }
}
